home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The X-Philes (2nd Revision)
/
The X-Philes Number 1 (1995).iso
/
xphiles
/
hp48_1
/
least.sq2
< prev
next >
Wrap
Text File
|
1995-03-23
|
6KB
|
212 lines
Article 5657 of comp.sys.handhelds:
Path: en.ecn.purdue.edu!noose.ecn.purdue.edu!samsung!zaphod.mps.ohio-state.edu!sdd.hp.com!spool.mu.edu!snorkelwacker.mit.edu!bloom-beacon!eru!hagbard!sunic!kuling!henrikj
From: henrikj@kuling.UUCP (Henrik Johansson)
Newsgroups: comp.sys.handhelds
Subject: HP48 Least Squares Approximation
Message-ID: <2014@kuling.UUCP>
Date: 7 Apr 91 09:07:50 GMT
Reply-To: henrikj@kuling.UUCP (Henrik Johansson)
Organization: Upsala University, Sweden
Lines: 198
Least Squares Approximations on the HP48
In a way the statistic functions on the HP48 are quite good, but
if one wants to do some more complicated curve fitting the built-in
functions are unsatisfactory. Therefore I sat down and rewrote a
LISP program I wrote a few years ago for more general least squares
approximation problems into HP48 notation.
Suppose you have a set of points in the plane and you know that
they will fit well on a line, then you use the built-in functions
to solve the problem. But if they seem to fit better on a
function like "y = a*x^2 + b*x + c + d*ln(x)" or something? You don't.
This program makes it possible to solve a problem like that above.
The most general form of input 0is
y = a_1 * f_1(x_1, x_2, ..., x_m) +
a_2 * f_2(x_1, x_2, ..., x_m) +
... +
a_n * f_n(x_1, x_2, ..., x_m)
together with a set of data points. I call the x_i's the independent
variables, and y the dependent variable.
Example 1:
x: 1 2 3 4
y: 3 6 11 18
and y is a quadratic function like "a*x^2 + b*x + c" then you enter
Stack level
4: [ 1 2 3 4 ] x values
3: [ 3 6 11 18 ] y values (be sure that they agree by index)
2: { 'X^2' X 1 } f_i's
1: 'X' independent variable
execute LSQ and get the following result
(symbolic mode on) (symbolic mode off)
Stack level Stack level
2: .999999999992 2: .999999999992
1: '.99999999967*X^2+ 1: [[ .99999999967 ]
1.6511E-9*X+ [ 1.6511E-9 ]
1.99999999835' [ 1.99999999835 ]]
The result is that a = 1, b = 0 and c = 2.
The number in level 2 tells how well the resulting coefficients (a_i)
make the total function correspond to the given set of data.
A value of 1 is a complete match, and 0 is a complete mismatch.
--------
It may be the case that you have a `t' that are dependent on more than
one variable.
Example 2:
x: 1 -8 5 1 2 3 3
y: 3 6 2 1 3 0 2
z: -5 7 4 1 4 6 1
t: -102 -123 108 -2 61 74 10
and a reasonable function to try is "t = a*x + b*x*z + c*y + d*y*z + e".
(It is always up to the user to suggest a function!)
Stack level
4: [[ 1 3 -5 ]
[ -8 6 7 ]
[ 5 2 4 ]
[ 1 1 1 ]
[ 2 3 4 ]
[ 3 0 6 ]
[ 3 2 1 ]]
3: [ -102 -123 108 -2 61 74 10 ]
2: { X 'X*Z' Y 'Y*Z' 1 }
1: { X Y Z }
execute LSQ and get the following result
(symbolic mode on) (symbolic mode off)
Stack level Stack level
2: .999999999998 2: .999999999998
1: '3.00000000006*X+ 1: [[ 3.0000000006 ]
3.99999999999*(X*Z)- [ 3.9999999999 ]
6.000000000007*Y+ [ -6.00000000007 ]
4.000000000001*(Y*Z) [ 4.00000000001 ]
-7.000000000064' [ -7.00000000064 ]]
The result is that a = 3, b = 4, c = 6, d = 4 and e = -7.
--------
As you can see there is two ways to enter the x_i-values. If there is
only one dependent variable (m = 1) then its values may be entered in
a vector (eg. [ 1 2 3 4 5 ]) but if there are more than one, their values
has to be entered as in the Example 2. The y-values should be entered in
a vector. If there is only one dependent variable it can be entered
as 'X' instead of { X } in stack level 1.
If you want to find a and b in "a*x^b", then you have to do some
pre- and post-processing on the data to make it linear in a and b.
It is always the case that `y' is a linear
function of the a_i's (unfortunately). It had
been nice to have a completely general function...
The method I used in the program uses much memory but is somewhat faster
than the other method I could have used (that would have used a lesser
amount of memory). By this I don't mean that the program is fast...:-)
Some parts of the code can surely be rewritten for speed, but I didn't
feel the need to make it better than it is - I'm satisfied with the
result. Almost. As can be seen by the examples, the numerical accuracy
could have been better but I don't know how a general method for increasing
the accuracy could be implemented. If anyone has any ideas - please
let me know. I haven't consulted any textbooks about this problem so
good advice about what to read would be welcome.
If there are any questions, suggestions, comments or bugs - just write
me a mail, and I will answer as soon as I can.
---------
The program takes up 672.5 bytes, and has ckecksum #7543h when stored in a
variable called 'LSQ'.
---- cut here ----
%%HP: T(3)A(R)F(.);
\<<
\<<
IF DUP SIZE
SIZE 1 ==
THEN OBJ\-> 1
SWAP + \->ARRY TRN
CONJ
END
\>> \-> fix
\<<
IF DUP TYPE 5 \=/
THEN 1 \->LIST
END 4 ROLL fix
EVAL DUP SIZE OBJ\->
DROP \-> yv fv vv xv
r c
\<<
IF r yv SIZE
1 GET - c vv SIZE -
OR
THEN
"Size(s) mismatch"
DOERR
END 1 fv SIZE
FOR I xv OBJ\->
DROP fv I GET \-> f
\<< r 1
FOR J vv
OBJ\-> DROP 1 c
FOR K c
2 + K - ROLL SWAP
STO
NEXT f
EVAL J 1 - c * 1 +
r + J - ROLLD -1
STEP
\>>
NEXT fv SIZE
r 2 \->LIST \->ARRY DUP
yv fix EVAL DUP 4
ROLLD * DUP 4 ROLLD
SWAP DUP TRN CONJ *
/ DUP 4 ROLLD TRN
CONJ SWAP 3 ROLLD
SWAP * OBJ\-> DROP
SWAP DUP 1 CON TRN
SWAP * OBJ\-> DROP SQ
r / SWAP OVER -
SWAP yv DUP DOT -
NEG / SWAP
IF -3 FC?
THEN \-> cv
\<< 0 1 fv
SIZE
FOR I cv
I 1 2 \->LIST GET fv
I GET * +
NEXT
\>>
END
\>>
\>>
\>>
---- and cut here too ----
-------
henrikj@kuling.docs.uu.se Henrik Johansson
(Nothing fancy about me - at least not in the signature)